﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine.Networking;

namespace Core
{
    public class AssetSystem : EntitySystem, IAwake
    {
        private AssetBundleManifest manifest;

        public string persistentRoot;
        public string streamingRoot;

        public SaveFiles saveConfig;
        public const string SAVE_FILES_NAME = "save_files.json";

        public IAssetHelper assetHelper;
        public IAssetCounterHelper assetCounterHelper;
#if ENABLE_HCLR
        public IHclrHelper hclrHelper;
#endif

        public void Awake()
        {
            assetHelper = GetHelper<IAssetHelper>();
            assetCounterHelper = GetHelper<IAssetCounterHelper>();

#if ENABLE_HCLR
            hclrHelper = GetHelper<IHclrHelper>();
            Subscribe<LoadHclrMetadatas>(LoadHclrMetadatas);
            Subscribe<LoadHclrDlls>(LoadHclrDlls);
#endif

#if ENABLE_WECHAT
            persistentRoot = $"{WeChatWASM.WX.env.USER_DATA_PATH}/{GameConst.PACKAGE_VERSION_PATH}";
#else
            persistentRoot = $"{Application.persistentDataPath}/{GameConst.PACKAGE_VERSION_PATH}";
#endif
            streamingRoot = $"{Application.streamingAssetsPath}/{GameConst.PACKAGE_VERSION_PATH}";

            Subscribe<DecompressStreamingAssets>(DecompressStreamingAssets);
            Subscribe<UpdateAssetsVersion>(UpdateAssetsVersion);
            Subscribe<LoadSceneAssetBundleAsync>(LoadSceneAssetBundleAsync);
            Subscribe<UnloadAsset>(UnloadAsset);
            Subscribe<ForceGCAsset>(ForceGCAsset);
            Subscribe<LoadAssetBundleManifest>(LoadAssetBundleManifest);
            Subscribe<LoadAssetBundleManifestAsync>(LoadAssetBundleManifestAsync);
            SubscribeAllLoadAssetEvent();
        }

#if ENABLE_HCLR

        private void LoadHclrMetadatas(ref LoadHclrMetadatas data)
        {
            hclrHelper.LoadMetadatas();
        }

        private void LoadHclrDlls(ref LoadHclrDlls data)
        {
            var dllPaths = new List<string>();
            foreach (var dllName in saveConfig.hclrDlls)
            {
                var dllPersistentPath = GetAssetBundlePersistentPath(dllName);
                if (string.IsNullOrEmpty(dllPersistentPath))
                {
                    Debug.LogError($"找不到dll版本信息:{dllName}");
                    return;
                }
                dllPaths.Add(dllPersistentPath);
            }
            hclrHelper.LoadDlls(dllPaths);
        }

#endif
        private void SubscribeAllLoadAssetEvent()
        {
            SubscribeLoadAsset<UnityEngine.Object>();
            SubscribeLoadAsset<GameObject>();
            SubscribeLoadAsset<Material>();
            SubscribeLoadAsset<Texture2D>();
            SubscribeLoadAsset<Texture>();
            SubscribeLoadAsset<Shader>();
            SubscribeLoadAsset<TextAsset>();
            SubscribeLoadAsset<Sprite>();
        }

        public void SubscribeLoadAsset<T>() where T : UnityEngine.Object
        {
            Subscribe<LoadAssetAsync<T>>(LoadAssetAsync);
            Subscribe<LoadAsset<T>>(LoadAsset);
        }

        public void ForceGCAsset(ref ForceGCAsset data)
        {
            assetCounterHelper.TryUnloadUnusedBundles();
            Resources.UnloadUnusedAssets();
            GC.Collect();
        }

        public void LoadAssetBundleManifest(ref LoadAssetBundleManifest data)
        {
            LoadAssetBundleManifest();
        }

        public async ETTask<LoadAssetBundleManifestAsync> LoadAssetBundleManifestAsync(LoadAssetBundleManifestAsync data)
        {
            await LoadAssetBundleManifestAsync();
            return data;
        }

        #region 解压资源

        public void DecompressStreamingAssets(ref DecompressStreamingAssets data)
        {
            var saveConfigPath = $"{persistentRoot}/{SAVE_FILES_NAME}";
            if (assetHelper.IsFileExists(saveConfigPath))
            {
                var saveConfigContent = assetHelper.LoadString(saveConfigPath);
                saveConfig = Utils.JsonToObject<SaveFiles>(saveConfigContent);
            }
            else
            {
                saveConfig = new SaveFiles();
            }

            DecompressStreamingAssets();
        }

        /// <summary>
        /// 从StreamingAssets文件夹中解压资源
        /// </summary>
        public async void DecompressStreamingAssets()
        {
            // 读取版本信息
            var packagePath = $"{streamingRoot}/{GameConst.PACKAGE_FILE_NAME}";
            var packageContent = await assetHelper.LoadStringAsync(packagePath);

            if (string.IsNullOrEmpty(packageContent))
            {
                Debug.LogError($"找不到steamingAsset资源配置文件：{packagePath}");
                return;
            }

            var packageData = Utils.JsonToObject<PackageData>(packageContent);

            if (saveConfig.persistentVersion >= packageData.packageVersion)
            {
                new OnDecompressStreamingAssetsComplete().Publish();
                return;
            }

            SetResourceUrl(packageData.serverResourceUrl);

            // 读取资源配置
            var assetsContent = await assetHelper.LoadStringAsync
                ($"{streamingRoot}/{packageData.packageVersion}/{GameConst.ASSETS_FILE_NAME}");

            if (string.IsNullOrEmpty(assetsContent))
            {
                Debug.LogError("assetsContent is null");
                return;
            }

            var assetsData = Utils.JsonToObject<AssetsData>(assetsContent);
            var totalDecompressCount = assetsData.fileDatas.Count;
            var currentDecompressCount = 0;

#if ENABLE_HCLR
            var isSuccess = await hclrHelper.DecompressMetadatas();
            if (!isSuccess)
                return;
            saveConfig.hclrDlls = assetsData.hclrDlls;
#endif
            CompareSaveFiles(assetsData, out var needDeleteFiles, out var differentFiles);

            DeleteFiles(needDeleteFiles);

            // 解压资源
            foreach (var filePair in differentFiles)
            {
                var fileName = filePair.Key;
                var fileData = filePair.Value;

                var savePath = $"{persistentRoot}/{fileData.version}/{fileName}";

                // 从steamingAsset中加载
                var streamingFilePath = $"{streamingRoot}/{fileData.version}/{fileName}";
                var fileBytes = await assetHelper.LoadBytesAsync(streamingFilePath);
                if (fileBytes == null)
                {
                    Debug.LogError($"{streamingFilePath} does not exist.");
                    continue;
                }

                // 解压
                if (fileData.zipCrc32 > 0)
                    fileBytes = Utils.Decompress(fileBytes);

                // 保存
                await assetHelper.SaveBytesAsync(savePath, fileBytes);
                AddSaveFile(fileName, fileData.version);

                currentDecompressCount++;

                new OnDecompressStreamingAssetsProgress(currentDecompressCount, totalDecompressCount).Publish();
            }

            SetSaveVersion(packageData.packageVersion);

            new OnDecompressStreamingAssetsComplete().Publish();
        }

        #endregion

        #region 更新资源

        // 超时重试延迟时间（秒），避免重试太频繁造成性能瓶颈
        public float retryDelayTime = 1f;
        // 超时时间
        public static int[] time_list = new int[] {
            5, 10, 15
        };

        public ulong totalDownlaodSize;
        public ulong hadDownloadSize;

        public int totalDownloadCount;
        public int currentDownloadCount;
        public int updateVerion;

        /// <summary>
        /// 最大同时下载个数
        /// </summary>
        public int maxDownloadCount = 5;
        
        /// <summary>
        /// 最大异步处理资源个数
        /// </summary>
        public int maxAsyncAssetCount = 5;
        public int currentAsyncAssetCount = 0;

        public List<DownLoadData> retryDownloadDatas;
        public List<DownLoadData> waitDownloadDatas;
        public List<DownLoadData> downloadingDatas;

        public void UpdateAssetsVersion(ref UpdateAssetsVersion data)
        {
            CheckPackageVersion();
        }

        public async void CheckPackageVersion()
        {
            var packageContent = "";
            var requestCount = 0;
            var perTryTime = new WaitForSeconds(retryDelayTime);

            #region 获取远程版本信息

            var serverPackagePath = $"{saveConfig.serverResourceUrl}/{GameConst.PACKAGE_FILE_NAME}";

            // 多次重试
            while (requestCount < time_list.Length)
            {
                packageContent = await assetHelper.LoadStringAsync(serverPackagePath, time_list[requestCount]);
                if (!string.IsNullOrEmpty(packageContent))
                {
                    break;
                }
                else
                {
                    var failMsg = $"load resource url({serverPackagePath}) fail, retry request.";
                    Debug.LogError(failMsg);
                    requestCount++;
                    await perTryTime;
                }
            }

            if (string.IsNullOrEmpty(packageContent))
            {
                Debug.LogError("can not get packageContent");
                return;
            }

            var packageData = Utils.JsonToObject<PackageData>(packageContent);
            
            // 比较资源版本，如果版本更高，则需要更新
            if (packageData.packageVersion > saveConfig.persistentVersion)
            {
                updateVerion = packageData.packageVersion;
                CheckUpdateFiles();
            }
            else
            {
                new OnUpdateAssetsVersionComplete().Publish();
            }

            #endregion
        }

        /// <summary>
        /// 对比资源文件
        /// </summary>
        private async void CheckUpdateFiles()
        {
            Debug.Log($"check update data, {saveConfig.persistentVersion} -> {updateVerion}");

            // 加载远程资源文件
            var remotePath = $"{saveConfig.serverResourceUrl}/{updateVerion}/{GameConst.ASSETS_FILE_NAME}";

            var requestCount = 0;
            var remoteContent = "";
            var perTryTime = new WaitForSeconds(retryDelayTime);

            // 多次重试
            while (requestCount < time_list.Length)
            {
                remoteContent = await assetHelper.LoadStringAsync(remotePath, timeout: time_list[requestCount]);
                if (!string.IsNullOrEmpty(remoteContent))
                {
                    break;
                }
                else
                {
                    var failMsg = $"load remote files fail, retry request:{remotePath}";
                    Debug.LogError(failMsg);
                    requestCount++;
                    await perTryTime;
                }
            }

            if (string.IsNullOrEmpty(remoteContent))
            {
                Debug.LogError("can not get remoteContent");
                return;
            }

            var remoteFiles = Utils.JsonToObject<AssetsData>(remoteContent);

#if ENABLE_HCLR
            saveConfig.hclrDlls = remoteFiles.hclrDlls;
#endif

            CompareSaveFiles(remoteFiles, out var needDeleteFiles, out var differentFiles);
            
            totalDownlaodSize = 0;
            foreach (var item in differentFiles.Values)
                totalDownlaodSize += item.size;

            DeleteFiles(needDeleteFiles);

            if (differentFiles.Count <= 0)
            {
                // 更新完成
                SetSaveVersion(updateVerion);
                new OnUpdateAssetsVersionComplete().Publish();
            }
            else
            {
                Debug.Log($"needDownloadFiles, size:{totalDownlaodSize}");
                // 下载
                StartDownload(differentFiles);
            }
        }

        public void CompareSaveFiles(AssetsData updateFiles, 
            out Dictionary<string, int> needDeleteFiles, 
            out Dictionary<string, FileData> differentFiles)
        {
            // 缓存资源配置
            needDeleteFiles = new Dictionary<string, int>(saveConfig.saveFiles);

            differentFiles = new Dictionary<string, FileData>();

            foreach (var updatePair in updateFiles.fileDatas)
            {
                var fileName = updatePair.Key;
                var fileData = updatePair.Value;

                if (needDeleteFiles.TryGetValue(fileName, out var oldVersion) && oldVersion == fileData.version)
                {
                    // 缓存中已存在，不需要更新
                    needDeleteFiles.Remove(fileName);
                }
                else
                {
                    // 缓存中没有，需要更新
                    differentFiles.Add(fileName, fileData);
                }
            }
        }

        public void DeleteFiles(Dictionary<string, int> fileDatas)
        {
            if (fileDatas.Count == 0)
                return;

            foreach (var pathPair in fileDatas)
            {
                var filePath = $"{persistentRoot}/{pathPair.Value}/{pathPair.Key}";
                assetHelper.DeleteFile(filePath);
                saveConfig.saveFiles.Remove(pathPair.Key);
            }

            SaveConfig();
        }

        public class DownLoadData
        {
            public string fileName;
            public FileData data;

            public string requestPath;
            public UnityWebRequest request;

            public int retryCount;
            public float retryDelayTime;
        }

        public void StartDownload(Dictionary<string, FileData> needDownloads)
        {
            retryDownloadDatas = new List<DownLoadData>();
            waitDownloadDatas = new List<DownLoadData>();
            downloadingDatas = new List<DownLoadData>();
            foreach (var item in needDownloads)
            {
                var data = new DownLoadData();

                data.requestPath = $"{saveConfig.serverResourceUrl}/{item.Value.version}/{item.Key}";
                var request = UnityWebRequest.Get(data.requestPath);
                request.timeout = time_list[data.retryCount];

                data.fileName = item.Key;
                data.data = item.Value;
                data.request = request;

                waitDownloadDatas.Add(data);
            }

            hadDownloadSize = 0;
            totalDownloadCount = waitDownloadDatas.Count;

            new StartCoroutine(Update()).Publish();
        }

        public IEnumerator Update()
        {
            while (!IsDownloadComplete())
            {
                UpdateRetryTime();
                TryStartNewRequest();
                CheckDownloadState();
                yield return null;
            }

            if (currentDownloadCount == totalDownloadCount)
                SetSaveVersion(updateVerion);
            
            new OnUpdateAssetsVersionComplete().Publish();
        }

        private bool IsDownloadComplete()
        {
            if (waitDownloadDatas.Count == 0 && downloadingDatas.Count == 0 && retryDownloadDatas.Count == 0 && currentAsyncAssetCount == 0)
                return true;

            return false;
        }

        private void UpdateRetryTime()
        {
            // 更新延迟重试下载时间
            for (int i = retryDownloadDatas.Count - 1; i >= 0; i--)
            {
                var current = retryDownloadDatas[i];
                current.retryDelayTime -= Time.deltaTime;
                if (current.retryDelayTime <= 0)
                {
                    current.retryDelayTime = 0;
                    retryDownloadDatas.RemoveAt(i);
                    waitDownloadDatas.Add(current);
                }
            }
        }

        private void TryStartNewRequest()
        {
            // 发起下载
            while (downloadingDatas.Count < maxDownloadCount 
                && waitDownloadDatas.Count > 0
                && currentAsyncAssetCount < maxAsyncAssetCount)
            {
                var index = waitDownloadDatas.Count - 1;
                var currentData = waitDownloadDatas[index];
                waitDownloadDatas.RemoveAt(index);

                currentData.request.SendWebRequest();
                downloadingDatas.Add(currentData);
            }
        }

        private void CheckDownloadState()
        {
            string failMsg = "";

            ulong downloadingSize = 0;

            for (int index = downloadingDatas.Count - 1; index >= 0; index--)
            {
                var download = downloadingDatas[index];

                if (download.request.isDone)
                {
                    downloadingDatas.RemoveAt(index);

                    if (download.request.result == UnityWebRequest.Result.Success)
                    {
                        currentAsyncAssetCount++;

                        // 下载完成
                        hadDownloadSize += download.request.downloadedBytes;

                        // crc检验
                        var downloadBytes = download.request.downloadHandler.data;
                        var crc32 = Utils.Bytes2CRC32(downloadBytes);

                        var checkCrc32 = download.data.crc32;
                        // 如果是压缩文件，则对比压缩文件的crc32
                        if (download.data.zipCrc32 > 0)
                            checkCrc32 = download.data.zipCrc32;

                        if (crc32 != checkCrc32)
                        {
                            currentAsyncAssetCount--;
                            failMsg = $"crc32 check fail:{download.request.url}";
                            Debug.LogError(failMsg);
                            download.request.Dispose();
                            continue;
                        }

                        // 解压
                        if (download.data.zipCrc32 > 0)
                            downloadBytes = Utils.Decompress(downloadBytes);

                        // 保存文件
                        assetHelper.SaveBytes($"{persistentRoot}/{download.data.version}/{download.fileName}", downloadBytes);
                        AddSaveFile(download.fileName, download.data.version);

                        currentDownloadCount++;
                        currentAsyncAssetCount--;

                        download.request.Dispose();
                    }
                    else
                    {
                        download.retryCount++;
                        // 下载失败
                        if (download.retryCount < time_list.Length)
                        {
                            // 尝试重下
                            Debug.LogError($"download fail:{download.request.url},{download.request.error},{download.retryCount}");
                            download.request.Dispose();
                            RetryDownload(download);
                        }
                        else
                        {
                            // 重试失败
                            failMsg = $"download file error:{download.request.url},{download.request.error}";
                            Debug.LogError(failMsg);
                            download.request.Dispose();
                            continue;
                        }
                    }
                }
                else
                {
                    downloadingSize += download.request.downloadedBytes;
                }
            }

            new OnUpdateAssetsVersionProgress(currentDownloadCount, totalDownloadCount, downloadingSize + hadDownloadSize, totalDownlaodSize).Publish();
        }

        public void RetryDownload(DownLoadData downLoad)
        {
            downLoad.retryDelayTime = retryDelayTime;
            downLoad.request = UnityWebRequest.Get(downLoad.requestPath);
            downLoad.request.timeout = time_list[downLoad.retryCount];
            retryDownloadDatas.Add(downLoad);
        }

        #endregion

        #region 缓存信息

        public void AddSaveFile(string fileName, int version)
        {
            saveConfig.saveFiles[fileName] = version;
            SaveConfig();
        }

        public void SetSaveVersion(int version)
        {
            saveConfig.persistentVersion = version;
            SaveConfig();
        }

        public void SetResourceUrl(string url)
        {
            saveConfig.serverResourceUrl = url;
            SaveConfig();
        }

        public void SaveConfig()
        {
            var json = Utils.ObjectToJson(saveConfig);
            assetHelper.SaveString($"{persistentRoot}/{SAVE_FILES_NAME}", json);
        }

        #endregion

        #region AssetBundle

        private string GetAssetBundlePersistentPath(string bundleName)
        {
            if (!saveConfig.saveFiles.TryGetValue(bundleName, out var version))
                return "";
            return $"{persistentRoot}/{version}/{bundleName}";
        }

        #region 同步

        private void LoadAssetBundleManifest()
        {
            var path = GetAssetBundlePersistentPath(GameConst.MANIFEST_NAME);
            AssetBundle abm = assetHelper.LoadAssetBundle(path);
            if (abm == null)
            {
                Debug.LogError($"abm is null:{path}");
                return;
            }

            manifest = abm.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        public AssetBundle LoadAssetBundle(string bundleName)
        {
            var bundle = assetCounterHelper.GetAssetBundle(bundleName);
            if (bundle != null)
                return bundle;

            LoadAssetBundleDependencies(bundleName);
            return LoadSingleAssetBundle(bundleName);
        }

        private void LoadAssetBundleDependencies(string bundleName)
        {
            var dependencyNames = manifest.GetAllDependencies(bundleName);
            assetCounterHelper.AddBundleDependencies(bundleName, dependencyNames);

            foreach (var dependencyName in dependencyNames)
                LoadSingleAssetBundle(dependencyName);
        }

        private AssetBundle LoadSingleAssetBundle(string bundleName)
        {
            var bundle = assetCounterHelper.GetAssetBundle(bundleName);
            if (bundle != null)
                return bundle;

            var bundlePersistentPath = GetAssetBundlePersistentPath(bundleName);
            if (string.IsNullOrEmpty(bundlePersistentPath))
            {
                Debug.LogError($"找不到资源版本信息:{bundleName}");
                return null;
            }

            bundle = assetHelper.LoadAssetBundle(bundlePersistentPath);
            if (bundle == null)
            {
                Debug.LogError($"资源包加载失败:{bundleName}");
                return null;
            }

            assetCounterHelper.SetBundle(bundleName, bundle);
            return bundle;
        }

        #endregion

        #region 异步

        public async ETTask<AssetBundle> LoadAssetBundleAsync(string bundleName)
        {
            var assetBundle = await assetCounterHelper.GetAssetBundleAsync(bundleName);
            if (assetBundle != null)
                return assetBundle;

            await LoadAssetBundleDependenciesAsync(bundleName);
            return await LoadSingleAssetBundleAsync(bundleName);
        }

        private async ETTask LoadAssetBundleDependenciesAsync(string bundleName)
        {
            var dependencies = manifest.GetAllDependencies(bundleName);
            assetCounterHelper.AddBundleDependencies(bundleName, dependencies);

            foreach (var bundle in dependencies)
                await LoadSingleAssetBundleAsync(bundle);
        }

        private async ETTask LoadAssetBundleManifestAsync()
        {
            var path = GetAssetBundlePersistentPath(GameConst.MANIFEST_NAME);
            AssetBundle abm = await assetHelper.LoadAssetBundleAsync(path);
            if (abm == null)
            {
                Debug.LogError($"abm is null:{path}");
                return;
            }

            manifest = abm.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        private async ETTask<AssetBundle> LoadSingleAssetBundleAsync(string bundleName)
        {
            var assetBundle = await assetCounterHelper.GetAssetBundleAsync(bundleName);
            if (assetBundle != null)
                return assetBundle;

            var bundlePersistentPath = GetAssetBundlePersistentPath(bundleName);
            if (string.IsNullOrEmpty(bundlePersistentPath))
            {
                Debug.LogError($"找不到资源版本信息:{bundleName}");
                return null;
            }

            var loadingTask = assetHelper.LoadAssetBundleAsync(bundlePersistentPath);
            assetCounterHelper.SetLoadingTask(bundleName, loadingTask);

            var bundle = await loadingTask;
            if (bundle == null)
            {
                Debug.LogError($"资源包加载失败:{bundleName}");
                return null;
            }

            assetCounterHelper.SetBundle(bundleName, bundle);
            return bundle;
        }

        private async ETTask<LoadSceneAssetBundleAsync> LoadSceneAssetBundleAsync(LoadSceneAssetBundleAsync data)
        {
            var bundleName = GetAssetBundleNameByAssetPath(data.scenePath, out var _);
            assetCounterHelper.AddAssetCount(data.scenePath, bundleName);

            data.sceneBundle = await LoadAssetBundleAsync(bundleName);

            return data;
        }

        #endregion

        #endregion

        #region Assets

        internal void UnloadAsset(ref UnloadAsset data)
        {
            assetCounterHelper.ReduceAssetCount(data.assetPath, data.usingCount);
        }

        public string GetAssetBundleNameByAssetPath(string assetPath, out string assetName)
        {
            var index = assetPath.LastIndexOf('/');

            if (index == -1)
            {
                // 没有文件夹标识，是直接在PackageAssets目录下的
                assetName = assetPath;

                if (assetPath.EndsWith(".unity"))
                {
                    // 场景资源包
                    return $"{assetName}{GameConst.PACKAGE_EXTENSION}".ToLower();
                }
                else
                {
                    return $"{GameConst.PACKAGE_PATH}{GameConst.PACKAGE_EXTENSION}".ToLower();
                }
            }
            else
            {
                assetName = assetPath.Substring(index + 1);

                if (assetPath.EndsWith(".unity"))
                {
                    // 场景资源包
                    return $"{assetPath}{GameConst.PACKAGE_EXTENSION}".ToLower();
                }
                else
                {
                    var folderPath = assetPath.Substring(0, index);
                    return $"{folderPath}{GameConst.PACKAGE_EXTENSION}".ToLower();
                }
            }
        }

        #region 同步

        internal void LoadAsset(ref LoadAsset<UnityEngine.Object> data)
        {
            data.asset = LoadAsset<UnityEngine.Object>(data.assetPath);
        }

        internal void LoadAsset<T>(ref LoadAsset<T> data) where T : UnityEngine.Object
        {
            data.asset = LoadAsset<T>(data.assetPath);
        }

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetPath"></param>
        /// <returns></returns>
        private T LoadAsset<T>(string assetPath) where T : UnityEngine.Object
        {
            var bundleName = GetAssetBundleNameByAssetPath(assetPath, out string fileName);

            // 添加计数
            assetCounterHelper.AddAssetCount(assetPath, bundleName);

            var assetBunle = LoadAssetBundle(bundleName);
            if (assetBunle == null)
                return null;

            var asset = assetBunle.LoadAsset<T>(fileName);
            if (asset == null)
            {
                Debug.Log($"can not load asset '{fileName}' in assetbunle '{bundleName}'");
                return null;
            }

            return asset;
        }

        #endregion

        #region 异步

        internal async ETTask<LoadAssetAsync<UnityEngine.Object>> LoadAssetAsync(LoadAssetAsync<UnityEngine.Object> data)
        {
            data.asset = await LoadAssetAsync<UnityEngine.Object>((string)data.assetPath);
            return data;
        }

        internal async ETTask<LoadAssetAsync<T>> LoadAssetAsync<T>(LoadAssetAsync<T> data) where T : UnityEngine.Object
        {
            data.asset = await LoadAssetAsync<T>((string)data.assetPath);
            return data;
        }

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="assetPath"></param>
        /// <returns></returns>
        private async ETTask<T> LoadAssetAsync<T>(string assetPath) where T : UnityEngine.Object
        {
            var bundleName = GetAssetBundleNameByAssetPath(assetPath, out string fileName);

            // 添加计数
            assetCounterHelper.AddAssetCount(assetPath, bundleName);

            var assetBunle = await LoadAssetBundleAsync(bundleName);
            if (assetBunle == null)
                return null;

            var asset = assetBunle.LoadAsset<T>(fileName);
            if (asset == null)
            {
                Debug.Log($"can not load asset '{fileName}' in assetbunle '{bundleName}'");
                return null;
            }

            return asset;
        }

        #endregion

        #endregion
    }

    /// <summary>
    /// 尝试解压StreamingAssets中的资源包
    /// </summary>
    public struct DecompressStreamingAssets : IEventData
    {

    }

    /// <summary>
    /// 解压StreamingAssets中的资源包进度
    /// </summary>
    public struct OnDecompressStreamingAssetsProgress : IEventData 
    { 
        public int totalCount;
        public int currentCount;

        [System.Diagnostics.DebuggerHidden]
        public OnDecompressStreamingAssetsProgress(int currentCount, int totalCount)
        {
            this.currentCount = currentCount;
            this.totalCount = totalCount;
        }
    }

    /// <summary>
    /// 解压StreamingAssets中的资源包完成
    /// </summary>
    public struct OnDecompressStreamingAssetsComplete : IEventData
    {
        
    }

    /// <summary>
    /// 更新资源版本
    /// </summary>
    public struct UpdateAssetsVersion : IEventData
    {

    }

    public struct OnUpdateAssetsVersionProgress : IEventData
    {
        public int totalCount;
        public int currentCount;
        public ulong totalSize;
        public ulong currentSize;

        public OnUpdateAssetsVersionProgress(int currentCount, int totalCount, ulong currentSize, ulong totalSize)
        {
            this.currentCount = currentCount;
            this.totalCount = totalCount;
            this.currentSize = currentSize;
            this.totalSize = totalSize;
        }
    }

    public struct OnUpdateAssetsVersionComplete : IEventData
    {

    }

    [Serializable]
    public class PackageData
    {
        public int packageVersion;
        public string serverResourceUrl;
    }

    [System.Serializable]
    public class AssetsData
    {
        public int version;

#if ENABLE_HCLR
        public List<string> hclrDlls;
#endif

        public Dictionary<string, FileData> fileDatas;
    }

    [Serializable]
    public class FileData
    {
        public int version;
        public long crc32;
        public long zipCrc32;
        public ulong size;
    }

    [Serializable]
    public class SaveFiles
    {
        public int persistentVersion;
        public string serverResourceUrl;

#if ENABLE_HCLR
        public List<string> hclrDlls;
#endif

        public Dictionary<string, int> saveFiles = new Dictionary<string, int>();
    }

    internal struct LoadAsset<T> : IEventData where T : UnityEngine.Object 
    {
        public string assetPath;

        [System.Diagnostics.DebuggerHidden]
        public LoadAsset(string assetPath)
        {
            this.assetPath = assetPath;
            this.asset = default(T);
        }

        public T asset;
    }

    internal struct LoadAssetAsync<T> : IEventData where T : UnityEngine.Object
    {
        public string assetPath;

        [System.Diagnostics.DebuggerHidden]
        public LoadAssetAsync(string assetPath)
        {
            this.assetPath = assetPath;
            this.asset = default(T);
        }

        public T asset;
    }

    internal struct UnloadAsset : IEventData
    {
        public string assetPath;
        public int usingCount;

        public UnloadAsset(string assetPath) : this(assetPath, 1) { }

        [System.Diagnostics.DebuggerHidden]
        public UnloadAsset(string assetPath, int usingCount)
        {
            this.usingCount = usingCount;
            this.assetPath = assetPath;
        }
    }

    internal struct LoadSceneAssetBundleAsync : IEventData
    {
        public string scenePath;

        [System.Diagnostics.DebuggerHidden]
        public LoadSceneAssetBundleAsync(string scenePath)
        {
            this.scenePath = scenePath;
            this.sceneBundle = null;
        }

        public AssetBundle sceneBundle;
    }

    public struct ForceGCAsset : IEventData { }

    public struct LoadAssetBundleManifest : IEventData { }
    public struct LoadAssetBundleManifestAsync : IEventData { }
}

